package dev.gu.study.aspect;

import java.lang.reflect.Method;
import java.util.Stack;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.stereotype.Component;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;

@Aspect
@Component
public class ControllerAspect {
	
	@Autowired
	private ApplicationContext context;

	public Object runLog(ProceedingJoinPoint pjp) {
		long beginTime = System.currentTimeMillis();
		Object result = null;
		Throwable ex = null;
		boolean exceptionLog = false;
		Stack<DataSourceTransactionManager> dataSourceTransactionManagerStack = new Stack<>();
		Stack<TransactionStatus> transactionStatuStack = new Stack<>();
		boolean tx = false;
		try {
			MultipleTx classTx = pjp.getTarget().getClass().getAnnotation(MultipleTx.class);

			MethodSignature signature = (MethodSignature) pjp.getSignature();
			Method method = signature.getMethod();
			MultipleTx methodTx = method.getAnnotation(MultipleTx.class);

			MultipleTx[] mtxes = { classTx, methodTx };
			tx = openTransaction(dataSourceTransactionManagerStack, transactionStatuStack, mtxes);
			result = pjp.proceed();
			if (tx) {
				commit(dataSourceTransactionManagerStack, transactionStatuStack);
			}
		} catch (Throwable e) {
//			TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
			if (tx) {
				rollback(dataSourceTransactionManagerStack, transactionStatuStack);
			}
			ex = e;
			e.printStackTrace();
			exceptionLog = true;
		} finally {
		}
		saveLog(pjp, beginTime, exceptionLog, ex);
		return result;
	}

	@Around("@within(org.springframework.stereotype.Controller)")
	public Object aroundController(ProceedingJoinPoint pjp) {
		return runLog(pjp);
	}

	@Around("@within(org.springframework.web.bind.annotation.RestController)")
	public Object aroundRestController(ProceedingJoinPoint pjp) {
		return runLog(pjp);
	}

	/**
	 * 保存日志
	 * 
	 * @param joinPoint
	 * @param time
	 */
	private void saveLog(ProceedingJoinPoint pjp, long beginTime, boolean exceptionLog, Throwable ex) {
//		long endTime = System.currentTimeMillis();
//		MethodSignature signature = (MethodSignature) pjp.getSignature();
//		Method method = signature.getMethod();
//		String clzName = pjp.getTarget().getClass().getName();
//		String methodName = method.getName();
//		StringBuilder loginfo = new StringBuilder();
//		loginfo.append("class:").append(clzName).append("\r\n").append("method:").append(methodName).append("\r\n")
//				.append("params:").append("\r\n");
//		Object[] args = pjp.getArgs();
//		if (args != null) {
//			for (Object o : args) {
//				if (o instanceof Object[]) {
//					loginfo.append(Arrays.asList((Object[]) o));
//				} else {
//					loginfo.append(o);
//				}
//				loginfo.append("\r\n");
//			}
//		}
//		if (exceptionLog) {
//			loginfo.append("exception at \r\n").append(Strings.parseException(ex));
//			log.error(aroundLog(loginfo));
//		} else {
//			long runTime = endTime - beginTime;
//			if (slowTime > 0 && runTime > slowTime) {
//				loginfo.append("starttime:" + LocalDateTimes.format(beginTime)).append("\r\n");
//				loginfo.append("finishtime:" + LocalDateTimes.format(beginTime)).append("\r\n");
//				log.warn(aroundLog(loginfo));
//			} else {
//				log.info(aroundLog(loginfo));
//			}
//		}
	}

	/**
	 * @author guweichao 2019-03-13
	 * @desc 为日志信息添加统一前后缀
	 * @param loginfo
	 * @return
	 */
	private String aroundLog(StringBuilder loginfo) {
		StringBuilder logstr = new StringBuilder();
		return logstr.append("\r\n").append("------------------------log.start--------------------------")
				.append("\r\n").append(loginfo).append("------------------------log.finish--------------------------")
				.append("\r\n").toString();
	}

	/**
	 * 开启事务处理方法
	 * 
	 * @param dataSourceTransactionManagerStack
	 * @param transactionStatuStack
	 * @param multiTransactional
	 * @return
	 */
	private boolean openTransaction(Stack<DataSourceTransactionManager> dataSourceTransactionManagerStack,
			Stack<TransactionStatus> transactionStatuStack, MultipleTx[] mtxes) {
		boolean[] re = { false };
		if (mtxes != null) {
			for (MultipleTx mtx : mtxes) {
				if (mtx != null) {
					String[] transactionMangerNames = mtx.value();
					if (transactionMangerNames != null) {
						for (String tname : transactionMangerNames) {
								re[0] = true;
								DataSourceTransactionManager dataSourceTransactionManager = (DataSourceTransactionManager) context.getBean(tname);
								TransactionStatus transactionStatus = dataSourceTransactionManager
										.getTransaction(new DefaultTransactionDefinition());
								transactionStatuStack.push(transactionStatus);
								dataSourceTransactionManagerStack.push(dataSourceTransactionManager);
						}
					}
				}
			}
		}
		return re[0];
	}

	/**
	 * 提交处理方法
	 * 
	 * @param dataSourceTransactionManagerStack
	 * @param transactionStatuStack
	 */
	private void commit(Stack<DataSourceTransactionManager> dataSourceTransactionManagerStack,
			Stack<TransactionStatus> transactionStatuStack) {
		while (!dataSourceTransactionManagerStack.isEmpty()) {
			dataSourceTransactionManagerStack.pop().commit(transactionStatuStack.pop());
		}
	}

	/**
	 * 回滚处理方法
	 * 
	 * @param dataSourceTransactionManagerStack
	 * @param transactionStatuStack
	 */
	private void rollback(Stack<DataSourceTransactionManager> dataSourceTransactionManagerStack,
			Stack<TransactionStatus> transactionStatuStack) {
		while (!dataSourceTransactionManagerStack.isEmpty()) {
			dataSourceTransactionManagerStack.pop().rollback(transactionStatuStack.pop());
		}
	}

}
